package com.zxjbyte.yiyi.framework.tenant.redis;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.zxjbyte.yiyi.framework.common.constant.RedisKeyDefine;
import com.zxjbyte.yiyi.framework.common.constant.StringPool;
import com.zxjbyte.yiyi.framework.common.core.CacheKey;
import com.zxjbyte.yiyi.framework.common.util.AuthUtil;
import com.zxjbyte.yiyi.framework.tenant.config.TenantProperties;

import java.time.Duration;

/**
 *多租户拓展的 RedisKeyDefine 实现类
 *
 * 由于 Redis 不同于 MySQL 有 column 字段，无法通过类似 WHERE tenant_id = ? 的方式过滤
 * 所以需要通过在 Redis Key 上增加后缀的方式，进行租户之间的隔离。具体的步骤是：
 * 1. 假设 Redis Key 是 user:%d，示例是 user:1；对应到多租户的 Redis Key 是 user:%d:%d， 示例 user:1:000000
 * 2. 使用方式TenantRedisKeyDefine.getKey(Object... suffix)
 * @Author zhangxingjia
 * @Date 2023/4/3 15:28
 * @Version: 1.0
 */
public class TenantRedisKeyDefine {

    /**
     * 多租户的 KEY 模板
     */
    private static final String TENANT_KEY_SUFFIX_TEMPLATE  = StringPool.COLON + "%s";

    /**
     * ThreadLocal 用于存储线程特定的租户ID
     */
    private static final ThreadLocal<String> TENANT_ID = new ThreadLocal<>();

    /**
     * 静态内部类实现Holder模式
     */
    private static class InstanceHolder {
        private static final TenantRedisKeyDefine INSTANCE = createInstance();

        private static TenantRedisKeyDefine createInstance() {
            TenantProperties properties = SpringUtil.getBean(TenantProperties.class);
            return new TenantRedisKeyDefine(properties);
        }
    }

    private final TenantProperties properties;

    private TenantRedisKeyDefine(TenantProperties properties) {
        this.properties = properties;
    }

    /**
     * 获取单例实例
     */
    private static TenantRedisKeyDefine getInstance() {
        return InstanceHolder.INSTANCE;
    }

    /**
     * 构建多租户 Redis Key
     * 自动追加租户后缀（若启用租户模式）
     *
     * @param parts Key 组成部分
     */
    public static String buildKey(Object... parts) {
        return appendTenantSuffix(RedisKeyDefine.buildKey(parts));
    }

    /**
     * 构建带过期时间的多租户 Redis Key
     *
     * @param expire 过期时间
     * @param parts  Key 组成部分
     */
    public static CacheKey buildKey(Duration expire, Object... parts) {
        String key = RedisKeyDefine.buildKey(parts);
        key = appendTenantSuffix(key);
        return new CacheKey(key, expire);
    }

    /**
     * 构建指定租户的 Redis Key
     * 临时设置 tenantId，在 finally 中清理
     */
    public static String buildKeyWithSpecifiedTenant(String tenantId, Object... parts) {
        try {
            TENANT_ID.set(tenantId);
            return buildKey(parts);
        } finally {
            TENANT_ID.remove();
        }
    }

    /**
     * 构建指定租户的 Redis Key（带过期时间）
     */
    public static CacheKey buildKeyWithSpecifiedTenant(String tenantId, Duration expire, Object... parts) {
        try {
            TENANT_ID.set(tenantId);
            return buildKey(expire, parts);
        } finally {
            TENANT_ID.remove();
        }
    }

    private boolean isTenantEnabled() {
        try {
            return BooleanUtil.isTrue(properties.getEnabled());
        } catch (Exception e) {
            return true;
        }
    }

    /**
     * 获取当前租户ID
     */
    private static String getCurrentTenantId() {
        String tenantId = TENANT_ID.get();
        return tenantId != null ? tenantId : AuthUtil.getTenantId();
    }

    /** 在 Key 尾部追加租户后缀 */
    private static String appendTenantSuffix(String key) {
        TenantRedisKeyDefine instance = getInstance();
        if (!instance.isTenantEnabled()) {
            return key; // 未启用租户模式
        }
        String tenantId = getCurrentTenantId();
        if (StrUtil.isBlank(tenantId)) {
            return key;
        }
        return key + String.format(TENANT_KEY_SUFFIX_TEMPLATE, tenantId);
    }
}
